package cn.edu.scnu.match.service;

import cn.edu.scnu.exception.CreateException;
import cn.edu.scnu.exception.SaveException;
import cn.edu.scnu.match.mapper.MatchMapper;
import cn.edu.scnu.match.mapper.RegisterMapper;
import cn.edu.scnu.pojo.MatchActivity;
import cn.edu.scnu.pojo.RegistrationForm;
import cn.edu.scnu.pojo.User;
import cn.edu.scnu.utils.LoginUtils;
import cn.edu.scnu.utils.MapperUtil;
import cn.edu.scnu.utils.PrefixKey;
import cn.edu.scnu.utils.RestTemplateUtils;
import cn.edu.scnu.vo.MatchStatus;
import cn.edu.scnu.vo.ResultCode;
import cn.edu.scnu.vo.SysResult;
import cn.edu.scnu.vo.UserTypes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.client.RestTemplate;
import redis.clients.jedis.JedisCluster;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Service
public class MatchService {
    @Autowired
    private MatchMapper matchMapper;
    @Autowired
    private JedisCluster jedis;
    @Autowired
    private RegisterMapper registerMapper;
    @Lazy
    @Resource(type = RestTemplate.class)
    private RestTemplate restTemplate;

    public List<MatchActivity> getAllMatchs() {
        return matchMapper.getAllMatchs();
    }

    public MatchActivity getMatchById(String matchId) {
        // 设置matchKey
        String matchKey = PrefixKey.MATCH_QUERY_PREFIX + matchId;
        String lock = PrefixKey.MATCH_UPDATE_PREFIX + matchId;
        MatchActivity match = null;

        try {
            // 判断该赛事的更新锁是否还存在
            if (jedis.exists(lock)) {
                // 如果还存在，则直接访问数据库
                return matchMapper.getMatchById(matchId);
            }
            // 判断是否存在缓存数据
            if (jedis.exists(matchKey)) {
                // 存在缓存数据，则直接读取缓存
                match = MapperUtil.MP.readValue(jedis.get(matchKey), MatchActivity.class);
            } else {
                // 不存在缓存数据，则直接从数据库读取，并存储在缓存中

                // 设置过期时间
                int seconds = 60 * 60 * 24 * 2;

                // 没有缓存，查询数据库，并将结果存储在缓存中
                match = matchMapper.getMatchById(matchId);
                String matchJson = MapperUtil.MP.writeValueAsString(match);// 将对象转换成String进行存储
                jedis.setex(matchKey, seconds, matchJson);  // 存储在缓存中
            }

            if(match == null) {
                throw new RuntimeException("赛事不存在");
            }

            // 增加访问量
            matchMapper.addViews(matchId);

            System.out.println(match.getRegStartTime().toString());

            return match;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public void createMatch(MatchActivity matchActivity, HttpServletRequest httpServletRequest) throws Exception {
        // 获取当前用户信息
        User user = LoginUtils.getUserInfo();

        // 判断用户类型
        if (user.getType() != UserTypes.ORGANIZE.type) {
            throw new CreateException("该类型用户无权限创建比赛");
        }

        // 查看比赛名称是否重复
        int exist = matchMapper.getMatchByName(matchActivity.getName());
        if (exist > 0) {
            // 比赛名称重复
            throw new CreateException("比赛名称重复");
        }
        try {
            // 生成比赛编号
            String matchId = UUID.randomUUID().toString();
            matchActivity.setId(matchId);

            long regStartTime = matchActivity.getRegStartTime().getTime(); // 报名开始时间
            long regEndTime = matchActivity.getRegEndTime().getTime(); // 报名截至时间
            long matchStartTime = matchActivity.getMatchStartTime().getTime(); // 比赛开始时间
            long matchEndTime = matchActivity.getMatchEndTime().getTime(); // 比赛截至时间

            // 限制比赛时间的输入
            if(regEndTime <= regStartTime || matchEndTime <= matchStartTime) {
                throw new CreateException("截止时间应大于开始时间");
            }

            // 比较时间，初始化比赛状态
            long currentTime = System.currentTimeMillis(); // 计算机当前时间
            if (currentTime < regStartTime) {
                // 即将报名
                matchActivity.setStatus(MatchStatus.ENROLLING_SOON.status);
            } else if(currentTime >= regStartTime && currentTime <= regEndTime) {
                // 立即报名
                matchActivity.setStatus(MatchStatus.REGISTER_NOWS.status);
            } else if(currentTime > regEndTime) {
                // 报名结束
                matchActivity.setStatus(MatchStatus.REGISTRATION_ENDS.status);
            }

            // 获取组织名称
            SysResult result = restTemplate.getForObject("http://userService/orgs/" + user.getUsername(), SysResult.class);
            Map<String, String> organizer = null;
            organizer = (Map<String, String>) result.getData();
            matchActivity.setOrgName(organizer.get("nickname"));

            // 设置访问量，默认初始为0
            matchActivity.setViews(0);

            matchMapper.createMatch(matchActivity);
        } catch (Exception e) {
            // 创建赛事失败后删除图片
            e.printStackTrace();
            RestTemplateUtils.exchangeWithToken(restTemplate, httpServletRequest, "http://picService/delpic?picpath=" + matchActivity.getImgRecommend(), HttpMethod.DELETE);
            RestTemplateUtils.exchangeWithToken(restTemplate, httpServletRequest, "http://picService/delpic?picpath=" + matchActivity.getImgPoster(), HttpMethod.DELETE);
            throw new RuntimeException("创建赛事失败");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveMatch(MatchActivity matchActivity) throws SaveException {
        String matchKey = PrefixKey.MATCH_QUERY_PREFIX + matchActivity.getId();

        // 判断用户是否有修改权限
        User user = LoginUtils.getUserInfo();// 获取当前用户
        if (!matchActivity.getOrgId().equals(user.getId()) && user.getType() != UserTypes.ADMIN.type) {
            // 如果请求修改的用户不是该赛事的创建用户或管理员，则不允许修改
            throw new SaveException("当前用户无权限修改");
        }

        long regStartTime = matchActivity.getRegStartTime().getTime(); // 报名开始时间
        long regEndTime = matchActivity.getRegEndTime().getTime(); // 报名截至时间
        long matchStartTime = matchActivity.getMatchStartTime().getTime(); // 比赛开始时间
        long matchEndTime = matchActivity.getMatchEndTime().getTime(); // 比赛截至时间

        // 限制比赛时间的输入
        if(regEndTime <= regStartTime || matchEndTime <= matchStartTime) {
            throw new SaveException("截止时间应大于开始时间");
        }
        if (matchActivity.getMaxNumber() < matchActivity.getNumber()) {
            throw new SaveException("最大可报名人数不能小于已报名人数");
        }

        // 比较时间，修改比赛状态
        long currentTime = System.currentTimeMillis(); // 计算机当前时间
        if (currentTime < regStartTime) {
            // 即将报名
            matchActivity.setStatus(MatchStatus.ENROLLING_SOON.status);
        } else if(currentTime >= regStartTime && currentTime <= regEndTime) {
            // 立即报名
            matchActivity.setStatus(MatchStatus.REGISTER_NOWS.status);
        } else if(currentTime > regEndTime) {
            // 报名结束
            matchActivity.setStatus(MatchStatus.REGISTRATION_ENDS.status);
        }

        // 判断修改后的名称是否符合规范
        MatchActivity match = matchMapper.getMatchById(matchActivity.getId());
        if(!match.getName().equals(matchActivity.getName())) {
            int exist = matchMapper.getMatchByName(matchActivity.getName());
            if(exist > 0) {
                throw new SaveException("比赛名称重复");
            }
        }

        // 更新前删除缓存
        Long remainTime = 60000L;
        if(jedis.exists(matchKey)) {
            remainTime = jedis.ttl(matchKey);
            jedis.del(matchKey);
        }

        // 加锁
        String lock = PrefixKey.MATCH_UPDATE_PREFIX + matchActivity.getId() + ".lock";
        jedis.setex(lock, Integer.parseInt(remainTime + ""), "");   // 设置超时时间为旧数据存在的剩余时间
        matchMapper.saveMatch(matchActivity);

        // 将锁释放
        jedis.del(lock);
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteMatch(String matchId, HttpServletRequest httpServletRequest) {
        String matchKey = PrefixKey.MATCH_QUERY_PREFIX + matchId;
        try {
            // 查看缓存中是否有该数据
            if (jedis.exists(matchKey)) {
                // 如果存在，则将缓存中的数据删除
                jedis.del(matchKey);
            }

            // 删除游戏相关评论
            SysResult result = RestTemplateUtils.exchangeWithToken(restTemplate, httpServletRequest, "http://commentService/comments/matchs/" + matchId, HttpMethod.DELETE);
            if (result.getStatus() == ResultCode.NO_CONTENT.code) {
                // 删除游戏
                matchMapper.deleteMatch(matchId);
            } else {
                throw new RuntimeException(result.getMsg());
            }
        } catch (Exception e) {
            // 数据库回滚操作
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw new RuntimeException("删除赛事失败");
        }
    }

    public List<MatchActivity> getMatchsByUserId(String userId) {
        // 在报名表信息中获取用户所报名的所有赛事的编号
        List<RegistrationForm> registrationForms = registerMapper.getRegistersByUserId(userId);
        List<MatchActivity> matchs = new ArrayList<>();

        // 遍历每个赛事编号，根据编号将查询的
        for (RegistrationForm registrationForm : registrationForms) {
            matchs.add(matchMapper.getMatchById(registrationForm.getMatchId()));
        }
        return matchs;
    }

    public List<MatchActivity> getMatchsByOrgId(String orgId) {
        List<MatchActivity> matchs = matchMapper.getMatchsByOrgId(orgId);
        return matchs;
    }
}
